#include "ipc/semaphore.h"
#include "ipc/mutex.h"
#include "tools/list.h"
#include "cpu/irq.h"

void mutex_init(mutex_t *p_mutex)
{
    p_mutex->owner = NULL;
    p_mutex->locked_count = 0;
    list_init(&p_mutex->wait_list);
}

void mutex_lock(mutex_t *p_mutex)
{
    irq_state_t state = irq_enter_protection();

    task_t *curr_task = get_current_task();
    if (p_mutex->locked_count == 0) {
        p_mutex->owner = curr_task;
        p_mutex->locked_count++;
    } else if (p_mutex->owner == curr_task) {
        p_mutex->locked_count++;
    } else {
        task_set_block(curr_task);
        list_insert_last(&p_mutex->wait_list, &curr_task->wait_node);
        task_dispatch();
    }

    irq_leave_protection(state);
}

void mutex_unlock(mutex_t *p_mutex)
{
    irq_state_t state = irq_enter_protection();

    task_t *curr_task = get_current_task();
    if (curr_task == p_mutex->owner) {
        if (--p_mutex->locked_count == 0) {
            p_mutex->owner = NULL;
            if (list_count(&p_mutex->wait_list) > 0) {
                list_node_t *node = list_remove_first(&p_mutex->wait_list);
                task_t *task = field_struct_addr(node, task_t, wait_node);
                p_mutex->owner = task;
                p_mutex->locked_count++;
                task_set_ready(task);
                task_dispatch();
            }
        }
    }
    irq_leave_protection(state);
}
